/*
 *   Copyright 2008 Francois Fernandes
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package com.googlecode.opendoclibrary.server.internal;

import java.util.Arrays;
import java.util.Dictionary;
import java.util.Hashtable;

import org.eclipse.osgi.framework.console.CommandInterpreter;
import org.eclipse.osgi.framework.console.CommandProvider;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.cm.ManagedService;
import org.osgi.service.cm.ManagedServiceFactory;
import org.osgi.util.tracker.ServiceTracker;

public class CommandLineExtensions implements CommandProvider
{

    private static final String NL = "\n";

    private final ServiceTracker configAdminTracker;

    private final BundleContext bundleContext;

    public CommandLineExtensions( BundleContext bundleContext )
    {
        this.bundleContext = bundleContext;
        configAdminTracker = new ServiceTracker( bundleContext, ConfigurationAdmin.class.getName(), null );
        configAdminTracker.open();
    }

    private boolean isEmpty( String n )
    {
        return n == null || n.trim().length() == 0;
    }

    public void _create( CommandInterpreter ci ) throws Exception
    {
        ConfigurationAdmin admin = (ConfigurationAdmin) configAdminTracker.getService();
        if ( admin == null )
        {
            ci.println( "Missing configuration admin. Unable to process further" );
            return;
        }

        String pid = ci.nextArgument();
        if ( isEmpty( pid ) )
        {
            ci.println( "missing service pid" );
            return;
        }

        ServiceReference ref = retrieveManagedServiceFactory( pid );
        if ( ref == null )
        {
            ci.println( "no such service: " + pid );
            return;
        }

        Configuration config = admin.createFactoryConfiguration( pid, ref.getBundle().getLocation() );

        Dictionary<String, String> serviceConfig = parseProperties( ci );
        if ( serviceConfig == null )
        {
            return;
        }
        config.update( serviceConfig );
    }

    public void _config( CommandInterpreter ci ) throws Exception
    {
        ConfigurationAdmin admin = (ConfigurationAdmin) configAdminTracker.getService();
        if ( admin == null )
        {
            ci.println( "Missing configuration admin. Unable to process further" );
            return;
        }

        String pid = ci.nextArgument();
        if ( isEmpty( pid ) )
        {
            ci.println( "missing service pid" );
            return;
        }

        // check the type of service
        ServiceReference ref = retrieveManagedService( pid );
        if ( ref == null )
        {
            ci.println( "no such service: " + pid );
            return;
        }
        Configuration config = admin.getConfiguration( pid, ref.getBundle().getLocation() );

        Dictionary<String, String> serviceConfig = parseProperties( ci );
        if ( serviceConfig == null )
        {
            return;
        }
        config.update( serviceConfig );
    }

    private Dictionary<String, String> parseProperties( CommandInterpreter ci )
    {
        String cfgEntry;
        Dictionary<String, String> serviceConfig = new Hashtable<String, String>();
        while ( !isEmpty( cfgEntry = ci.nextArgument() ) )
        {
            String[] parts = cfgEntry.split( "=" );
            if ( parts.length != 2 )
            {
                ci.println( "illegal format of proptery: '" + cfgEntry + "'" );
                return null;
            }
            serviceConfig.put( parts[0], parts[1] );
        }
        return serviceConfig;
    }

    protected ServiceReference retrieveManagedService( String pid ) throws InvalidSyntaxException
    {
        ServiceTracker tracker =
            new ServiceTracker( bundleContext, bundleContext.createFilter( "(&(" + Constants.OBJECTCLASS + "="
                            + ManagedService.class.getName() + ")(" + Constants.SERVICE_PID + "=" + pid + "))" ), null );
        tracker.open();
        ServiceReference ref = tracker.getServiceReference();
        tracker.close();
        return ref;
    }

    protected ServiceReference retrieveManagedServiceFactory( String pid ) throws InvalidSyntaxException
    {
        ServiceTracker tracker =
            new ServiceTracker( bundleContext,
                                bundleContext.createFilter( "(&(" + Constants.OBJECTCLASS + "="
                                                + ManagedServiceFactory.class.getName() + ")(" + Constants.SERVICE_PID
                                                + "=" + pid + "))" ), null );
        tracker.open();
        ServiceReference ref = tracker.getServiceReference();
        tracker.close();
        return ref;
    }

    /**
     * @param len
     * @return
     */
    protected String indent( int len )
    {
        char[] chars = new char[len];
        Arrays.fill( chars, ' ' );
        return new String( chars );
    }

    private boolean is( String arg, String expected )
    {
        return arg != null && expected.equals( arg );
    }

    @Override
    public String getHelp()
    {
        StringBuilder b = new StringBuilder( "---OpenDocLibrary commands---\n" );

        // create command
        b.append( "\tconfig - configure OpenDocLibrary objects." ).append( NL );
        b.append( "\t         The overall syntax of create is based on the following structure:" ).append( NL );
        b.append( NL );
        b.append( "\t         config <pid> [property1=value1] [property2=value2] ..." ).append( NL );
        b.append( "\tcreate - create OpenDocLibrary objects." ).append( NL );
        b.append( "\t         The overall syntax of create is based on the following structure:" ).append( NL );
        b.append( NL );
        b.append( "\t         create <pid> [property1=value1] [property2=value2] ..." ).append( NL );
        b.append( NL );
        return b.toString();
    }

    public void close()
    {
        if ( configAdminTracker != null )
            configAdminTracker.close();
    }
}
